#include "SypEngine.h"
#ifdef CHECKMEM
#include "mmgr.h"
#endif
namespace Syp
{

	Font* Font::createFont(const String& fontName)
	{
		return (new Font(fontName));
	}

	void Font::destroyFont(Font* font)
	{
		//We shouldnt delete it ourselfs, FontRegistry will take care of it.
		FontRegistry::getRegistry().removeEntry(font->name);	
	}

	Font::~Font()
	{
		//fontData should be registered with Renderer and be cleared automatically
		release();	
	}
	Font::Font(const String& fontName):Margin(5)
	{    
		this->name = fontName;
		fontWidths.resize(128 * sizeof(PShort));//reserve space for 128 character widths
		fontData = 0;
		//Add an instance
		m = MeshInstance::createMeshInstance();
		FontRegistry::getRegistry().addFont(this);

	}

	const String& Font::getFontName()
	{
		return name;
	}

	String Font::stripFormatting(const String& text)
	{	
		String result = text;
		size_t start,end;
		//Remove color information
		do
		{
		start = result.find_first_of("/c(");
		end = result.find_first_of(")") + 1;
		if(start != String::npos)
		{
			assert(end != String::npos);//assert cos closing brace is not found
		result.erase(start, end-start );
		}
		}while(start != String::npos);
		return result;
	}

	PInt Font::getMargin()
	{
		return Margin;
	}

	void Font::initFonts(const String& fileName,int size, int MARGIN)
	{
		Margin = MARGIN;
		
		TexOptions* to = new TexOptions();
		to->buildMipMaps = false;
		to->compressTextures = false;
		to->tex = 0;
		to->filename = fileName;
		to->filename +=  "_" + boost::lexical_cast<String>(size);
		//FreeType//////////////////////////////////////////////
		FT_New_Face( FontRegistry::getFTLibrary(),
					fileName.c_str(),
                       0,
                       &face );

		FT_Set_Char_Size(
            face,    /* handle to face object           */
            0,       /* char_width in 1/64th of points  */
            size*64,   /* char_height in 1/64th of points */
            72,     /* horizontal device resolution    */
            72 );   /* vertical device resolution      */

		int glyph_index = FT_Get_Char_Index( face, 87 );
		//We render 1 glyph to see how big it is, in this case W(87) 
		FT_Load_Glyph(//load 1 character
        face,          /* handle to face object */
        glyph_index,   /* glyph index           */
        FT_LOAD_DEFAULT );  

		if( face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
		{//if its not in bitmap form, render it
			FT_Render_Glyph( face->glyph,   /* glyph slot  */
                           FT_RENDER_MODE_NORMAL  ); /* render mode */
		}

		//=======================================================================
		//Now we create our texture
		int texH, texW;
		unsigned char* data = 0;
		texW = Math::nextPowerOf2( (face->glyph->bitmap.width + MARGIN) * 16 );//16 chars a row
		texH = Math::nextPowerOf2( (face->glyph->bitmap.rows + MARGIN) * 8 );//8 chars a column
		
		data = new unsigned char[ texH * texW ];//our texture
		for( int i = 0; i < texH * texW; ++i )
		{
			data[i] = 0;
		}
		//Generate the texture for this font
		PInt currX = 0;
		PInt currY = texH - (texH/8);
		
		PInt renderedThisLine = 0;
		//PInt glyphBytes;
		for( PInt i = 32; i < 128 ; ++i )
		{//for every character
			glyph_index = FT_Get_Char_Index( face, i );			
			FT_Load_Glyph(//load 1 character
			face,          /* handle to face object */
			glyph_index,   /* glyph index           */
			FT_LOAD_DEFAULT );  

			if( face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
			{//if its not in bitmap form, render it
				FT_Render_Glyph( face->glyph,   /* glyph slot  */
							  FT_RENDER_MODE_NORMAL  ); /* render mode */
			}
			std::cout<<"Rows for "<<static_cast<char>(i)<<" : "<<face->glyph->bitmap.rows<<" ADVANCE : "<<(face->glyph->metrics.horiAdvance >> 6)<<" TOP : ";
			std::cout<<face->glyph->bitmap_top<<"\n";

			for( int j = 0; j < face->glyph->bitmap.rows; ++j )//for every row
			{
				for(int k = 0; k < face->glyph->bitmap.width; ++k)
				{
					data[ ((  currY - j + face->glyph->bitmap.rows + ( face->glyph->bitmap_top - face->glyph->bitmap.rows ) + MARGIN ) * texW) + (currX + k + face->glyph->bitmap_left) ] = (face->glyph->bitmap.buffer[ j * face->glyph->bitmap.width + k ]) ;
				}
			}

			//this->fontWidths[glyph_index] = face->glyph->bitmap.pitch + MARGIN;
			this->fontWidths[ i - 32 ] = static_cast<short>(face->glyph->metrics.horiAdvance >> 6);
			currX += texW/16;
			++renderedThisLine;
			if( renderedThisLine >= 16 )
			{
				currY -= texH/8 + 1;//+1, dun ask me why, it just works, stupid thing made me waste 3 days
				renderedThisLine = 0;
			}
			

			




		}

		TextureObj* tex = TextureObj::loadFromMemory( to, texW, texH, data, Syp::ALPHAX	, Syp::ALPHA8 );

		

		////////////////////////////////////////////////////////

		OutputStream& err = ErrorLogger::getLogger();
		err<<"Initialising Font "<<name<<".\n";
		String matName = "FontMaterial: " + name;
		Material* ma = 0;
		ma = static_cast<Material*>(ResourceManager::getResourceManager().isResourceLoaded(matName));

		if(!ma)
		{//load if not loaded already
			ma = Material::createMaterial(matName);
			SubMaterial sm;
			sm.btype = Syp::BLEND;
			//we dont want the fonts to be affected by the lights
			sm.lit = false;
			//sm.useTexture(TextureObj::loadTexture(fileName,false,true,false));
			sm.useTexture(tex);
			sm.useEffect("GuiFF");


			ma->subMaterials.push_back(sm);	

		}
		IndexBlock ib;
		ib.materialID = 0;
		String meshName = "FontMesh: " + name;
		fontData = static_cast<NIndexedMesh*>(ResourceManager::getResourceManager().isResourceLoaded(meshName));
		if(!fontData)
		{
			fontData = NIndexedMesh::createNIndexedMesh(fileName + " bufferMesh",DYNAMIC);
			fontData->setRenderingFormat(FORMAT_QUADS);
			fontData->setVertexType(VC3T);	
			fontData->getDrawCallsRef().push_back(ib);
		}

		//Used as an overlay
		m->mode = ORTHO;
		m->useMesh(fontData);
		m->useMaterial(ma);

		FT_Done_Face( face );
		//	m->useTexture( TextureObj::loadTexture(name,false) );

		//InputStream fin;
		//fin.open(dat.c_str(),std::ios_base::binary|std::ios_base::in);

		//short* width = &fontWidths[0];
		////read in widths
		//fin.read(reinterpret_cast<char*>(width),128 * sizeof(PShort));
		//fin.close();

		fontSizeX = static_cast<float>(m->getMaterial()->subMaterials[0].getTexture()->getWidth() / 16);
		fontSizeY = static_cast<float>(m->getMaterial()->subMaterials[0].getTexture()->getHeight() / 8);
		
	}

	//Sets numOfVertices to 0, it does not clear the buffer so as to prevent memory allocation/deallocation every frame, buffer is reused every frame and will grow as needed.
	void Font::clearBuffer()
	{	
		fontData->getNumVerticesRef() = 0;
		//fontData->data.clear();

	}

	String Font::wordWrap(const String &text, float width)
	{
		assert(width>16.0f && "ARGHHHHHH, WTF are you doing?? width must be larger than 16.0f");
		size_t size = text.size(); 
		PInt currChar = 0;
		PInt numOfChar;
		PInt nls = 0;
		String t = text;
		while (currChar < size - 1)
		{
			if(fontWidth(text.substr(currChar,size - currChar )) < width )
			{
				break;
			}
			//Number of characters for this line, passes in a substring
			//starting from currChar == "Unprocessed string", number of Remaining chars

			numOfChar = numOfChars(text.substr(currChar,size - currChar + 1 ),width);
			while(text.at(currChar + numOfChar - 1) != ' ' )
				//while it is not a space, keep moving back
			{

				--numOfChar;
				if(numOfChar <= 1)
				{
					numOfChar =	numOfChars(text.substr(currChar,size - currChar),width);
					break;
				}
			}
			//nls (Newlines have to be taken into account)
			t.insert(currChar + numOfChar + nls,"\n");
			++nls;		
			currChar += numOfChar;//We have processed numOfChars in this loop

		}
		return t;
	}

	//Could D-Loop function
	PInt Font::numOfChars(const String& text,float width)
	{
		assert(width>16.0f && "ARGHHHHHH, WTF are you doing?? width must be larger than 16.0f");
		size_t numOfChars = text.size();
		PInt num = 0;
		float fWidth = 0.0f;
		for(PInt i=0;i<numOfChars;++i)
		{
			fWidth += fontWidths[ text.at(i) - 32 ];
			if(fWidth > width)
			{			
				break;
			}
			else
			{
				++num;
			}
		}
		return num;

	}

	float Font::fontWidth(const String& text)
	{
		const String pureTxt = Font::stripFormatting(text);
		size_t numOfChars = pureTxt.size();
		float fWidth = 0.0f;
		for(size_t i=0;i<numOfChars;++i)
		{
			fWidth += fontWidths[ pureTxt.at(i) - 32 ];
		}
		return fWidth;
	}

	MeshInstance* Font::generateStaticMesh(const String& text, float x, float y,float z,MeshInstance* fmi)
	{
		//we back up fontData
		NIndexedMesh* nim = fontData;
		fontData = NIndexedMesh::createNIndexedMesh( name + " mesh : " + text.substr(0,20), STATIC);

		///start				
		IndexBlock ib;
		ib.materialID = 0;
		
		fontData->setRenderingFormat(FORMAT_QUADS);
		fontData->setVertexType(VC3T);	
		fontData->getDrawCallsRef().push_back(ib);
		
		MeshInstance* mo = 0;
		if(fmi)
		{
            mo = fmi;
		}
		else
		{
		 mo = MeshInstance::createMeshInstance();
		}

		//Used as an overlay
		mo->mode = ORTHO;
		mo->useMesh(fontData);
		mo->useMaterial(m->getMaterial());
		//end



		draw(text,x,y,z);
		//reset the fonData to original mesh
		fontData = nim;
		return mo;
		
	}

	void Font::draw(const String &text, float x, float y,float z)
	{	

		assert(fontData && "Font has not been initialised");
		size_t length = text.length();
		PInt count = 0;//Needed to fix difference in size due to newline processing
		PInt bufferNeeded;
		//Each vertice takes up 8 floats meaning 32 floats for every new char
		//We add on to the current buffered vertices
		bufferNeeded = fontData->getNumVertices() * 8 + (32 * length);
		//Font will keep growing depending on size of string
		if(bufferNeeded > fontData->getDataRef().size())
		{
			fontData->getDataRef().resize( bufferNeeded );//reserve memory for new string			
		}	
		float currX = x;
		float currY = y;

		float cx,cy;
		char ch;//The character being rendered
		Color3f color;
		for(PInt i=0;i<length;++i)
		{
			ch = text.at(i);
			if(ch == '\n')
			{//If forward slash, it might be a special command
				currY -= fontSizeY;
				currX = x;
				continue;//dont process the char for drawing, go to next char
			}
			else if( ch == '/' && text.at(i+1) == 'c')
			{				
				std::stringstream ss(text, std::stringstream::in );
				ss.seekg(i+3, std::ios::beg);				
				ss>>color.r;
				ss>>color.g;
				ss>>color.b;
				i = ss.tellg();
				continue;

			}
			ch = ch - 32;//space is 32(1st char in texture)
			//UV coorinates 
			cx=static_cast<float>(ch%16)/16;//move from left to right
			cy= 0.875f - static_cast<float>(ch/16)/8  ;//move from 0 to 1 
			//X Y Z U V
			//We add numOfVertices so that we write after the currently buffered chars
			FloatArray::iterator it = fontData->getDataRef().begin() + fontData->getNumVertices() * 8;
			///Position
			*it = currX;
			++it;
			*it = currY;
			++it;
			*it = z;
			++it;
			///Color
			*it = color.r;
			++it;
			*it = color.g;
			++it;
			*it = color.b;
			++it;
			///UV
			*it = cx;
			++it;
			*it = cy;
			++it;

			//   _________ .
			*it = currX + fontSizeX;
			++it;
			*it = currY;
			++it;
			*it = z;
			++it;
			*it = color.r;
			++it;
			*it = color.g;
			++it;
			*it = color.b;
			++it;
			*it = cx + 0.0625f;
			++it;
			*it = cy;
			++it;

			//   ____________ .
			//  |
			//  |
			//  |
			//  |____________
			*it = currX + fontSizeX;
			++it;
			*it = currY + fontSizeY;
			++it;
			*it = z;
			++it;
			*it = color.r;
			++it;
			*it = color.g;
			++it;
			*it = color.b;
			++it;
			*it = cx + 0.0625f;
			++it;
			*it = cy + 0.125f;
			++it;


			//  .
			//  |
			//  |
			//  |
			//  |____________
			*it = currX;
			++it;
			*it = currY + fontSizeY;
			++it;
			*it = z;
			++it;
			*it = color.r;
			++it;
			*it = color.g;
			++it;
			*it = color.b;
			++it;
			*it = cx;
			++it;
			*it = cy + 0.125f;
			++it;

			currX += fontWidths[ch];
			//currX += fontSizeX;

			//i* 20 = 0 if i==0  data[0]
			//i* 20 = 20 if i==1 data[20]
			fontData->getNumVerticesRef() += 4;

			++count;//increase char count, might not be same as i when there is \n

		}	

		Renderer::getRenderer().updateMesh(fontData);
	}

	void Font::release()
	{
		m->clearAll();
		MeshInstance::destroyMeshInstance(m);
	}

}
